#include "GraphicsEngine.h"

GraphicsEngine::GraphicsEngine(HWND hWnd, MyCam& Cam)
{
	pD3D = NULL; 
	pd3dDevice = NULL;
	pVB = NULL;
	matStack = NULL;
	TestCam = &Cam;
	if(SUCCEEDED(InitD3D(hWnd)))
    {
		D3DXCreateMatrixStack(0, &matStack);
		//if(SUCCEEDED(InitVB()))
		//{
			InitCam();
			VertBuff = VertBuffer(pd3dDevice, pVB, matStack);
		//}
	}
}

GraphicsEngine::~GraphicsEngine(void)
{
	if(pD3D != NULL)
		pD3D->Release();
	if(pd3dDevice != NULL)
		pd3dDevice->Release();
	if(pVB != NULL)
		pVB->Release();
	if(matStack != NULL)
		matStack->Release();
}


//-----------------------------------------------------------------------------
// Name: InitD3D()
// Desc: Creates the D3D device
//-----------------------------------------------------------------------------
HRESULT GraphicsEngine::InitD3D(HWND hWnd)
{
	if(NULL == (pD3D = Direct3DCreate9(D3D_SDK_VERSION)))
		return E_FAIL;

	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory(&d3dpp, sizeof(d3dpp));
	d3dpp.Windowed = TRUE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
	d3dpp.EnableAutoDepthStencil = TRUE;
    d3dpp.AutoDepthStencilFormat = D3DFMT_D16;

	if(FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,
								 hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING,
								 &d3dpp, &pd3dDevice)))
	return E_FAIL;

	pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
	pd3dDevice->SetRenderState(D3DRS_ZENABLE, TRUE);
	
	return S_OK;
}

//-----------------------------------------------------------------------------
// Name: InitVB()
// Desc: Initilizes the vertex buffer
//-----------------------------------------------------------------------------
/*
HRESULT GraphicsEngine::InitVB()
{

// Now we create our blank Vertex buffer
	if(FAILED(pd3dDevice->CreateVertexBuffer(1000000*sizeof(CUSTOMVERTEX), 0, 
											D3DFVF_CUSTOMVERTEX,D3DPOOL_DEFAULT, 
											&pVB, NULL)))
	return E_FAIL;
	return S_OK;
		
}*/

LRESULT GraphicsEngine::Update(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	TestCam->HandleMessages(hWnd, uMsg, wParam, lParam);
	return S_OK;
}
//-----------------------------------------------------------------------------
// Name: UpdateWorld()
// Desc: Updates the world by moving the objects and refilling the vertexbuffer
//-----------------------------------------------------------------------------

HRESULT GraphicsEngine::UpdateWorld()
{
	
	//updates the camera
	TestCam->Update();
	pd3dDevice->SetTransform(D3DTS_VIEW, TestCam->GetViewMatrix());
	D3DXMATRIX mView;
	/*pd3dDevice->GetTransform(D3DTS_VIEW, &mView);
	matStack->LoadIdentity();
	matStack->MultMatrixLocal(&mView);*/
	return S_OK;
}

//-----------------------------------------------------------------------------
// Name: InitCam()
// Desc: Sets up the camera w/ a location look at point and projection matrix
//-----------------------------------------------------------------------------
void GraphicsEngine::InitCam()
{
	// initilizes the camera's location and where it is looking
	D3DXVECTOR3 vEyePt(0.0f, 3.0f, -5.0f);
	D3DXVECTOR3 vLookAtPt(0.0f, 0.0f, 0.0f);
	// Set View Matrix
	TestCam->SetViewParams(&vEyePt, &vLookAtPt);
	pd3dDevice->SetTransform(D3DTS_VIEW, TestCam->GetViewMatrix());
	// Set Projection Matrix
	float FOV = D3DX_PI/4;
	TestCam->SetProjParams(FOV ,1.0f, 1.0f, 100000.0f);
	pd3dDevice->SetTransform(D3DTS_PROJECTION, TestCam->GetProjectionMatrix());

	D3DXMATRIX mView;
	/*pd3dDevice->GetTransform(D3DTS_VIEW, &mView);
	matStack->LoadIdentity();
	matStack->LoadMatrix(&mView);*/
}

//-----------------------------------------------------------------------------
// Name: SetUpLights()
// Desc: Creates the lights
//-----------------------------------------------------------------------------
void GraphicsEngine::SetUpLights()
{
// Setting up the Lights
	D3DXVECTOR3 vecDir;
	D3DLIGHT9 light;
	ZeroMemory(&light, sizeof(D3DLIGHT9));
	light.Type = D3DLIGHT_DIRECTIONAL;
	light.Diffuse.r = 1.0f;
	light.Diffuse.g = 1.0f;
	light.Diffuse.b = 1.0f;
    vecDir = D3DXVECTOR3(1.0f, 1.0f, 1.0f);
	D3DXVec3Normalize((D3DXVECTOR3*)&light.Direction, &vecDir);
	light.Range = 1000.0f;
	pd3dDevice->SetLight(0,&light);
	pd3dDevice->LightEnable(0,TRUE);
	pd3dDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
// add ambient
	pd3dDevice->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(50,10,10));
}

//-----------------------------------------------------------------------------
// Name: Render()
// Desc: Draws the scene
//-----------------------------------------------------------------------------

VOID GraphicsEngine::Render(std::list<SceneRoot*> lFromTree)
{
	if(NULL == pd3dDevice)
		return;
	
//Clears Back and Z buffer
	pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 
						D3DCOLOR_XRGB(180,230,255), 1.0f, 0);
	
//Check to see if we lost control of the graphics card
/*	HRESULT DevTest = pd3dDevice->TestCooperativeLevel();
	if(FAILED(DevTest))
	{
		if(DevTest == D3DERR_DEVICELOST)
			return;
		if(DevTest == D3DERR_DEVICENOTRESET)
		{
		//	DeleteMyResources();
		//	pd3dDevice->Reset();
		//	InityMyResources();
		}
	}*/
// Creates the scene
	if(SUCCEEDED(pd3dDevice->BeginScene()))
	{
		SetUpLights();
        UpdateWorld();
		std::list<SceneRoot*>::const_iterator i;
		for(i=lFromTree.begin(); i != lFromTree.end(); ++i)
			(*i)->Update(&VertBuff);
//Ends the scene
		pd3dDevice->EndScene();
	}
	pd3dDevice->Present(NULL, NULL, NULL, NULL);
}
